package com.nvelickovic10.snake.main;

import java.awt.Color;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;

import org.apache.commons.net.ftp.FTPClient;

import com.nvelickovic10.snake.beans.engine.Engine;
import com.nvelickovic10.snake.beans.food.Bonus;
import com.nvelickovic10.snake.beans.food.BonusType;
import com.nvelickovic10.snake.beans.food.Food;
import com.nvelickovic10.snake.beans.game.HiScore;
import com.nvelickovic10.snake.beans.snake.Snake;
import com.nvelickovic10.snake.beans.snake.SnakeBits;
import com.nvelickovic10.snake.gui.GameWindow;

public class AppCore {

	private static AppCore instance = null;
	private Color boardColor;
	private final Color gridColor;
	private final Color borderColor;
	private final Color boardColorI;
	private final Color boardColorII;
	private final int width, height, left, right, top, bottom, factor;
	private boolean gameOn, developerVersion, offlineVersion, spaceDown,
			paused, loading, firstBonus = true;
	private int points, wallFaktor;
	private final int wd, hg;
	private Food food;
	private Bonus yellowBonus, blueBonus, magentaBonus, greenBonus;
	private ArrayList<Bonus> bonuses;
	private final Random rnd;
	private long speed, speedFaktor, cheatSpeed;
	private Engine engine;
	private GameWindow gw;
	private String username;
	private ArrayList<HiScore> hiscore;
	private FTPClient ftpClient;
	private ArrayList<SnakeBits> walls;

	public void restart() {
		speedFaktor = 300;
		speed = speedFaktor;
		setPoints(0);
		spaceDown = false;
		walls = new ArrayList<>();
		Snake.getInstance().restart();

		setGameOn(true);
		wallFaktor = 10;
		setFoodPosition();
	}

	private AppCore() {
		setOfflineVersion(true);
		setDeveloperVersion(false);

		if (!offlineVersion) {
			try {
				ftpClient = new FTPClient();
				ftpClient.connect("vedrana1.users.sbb.rs");
				ftpClient.login("vedrana1", "telenor1");
				ftpClient.enterLocalPassiveMode();
			} catch (SocketException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		walls = new ArrayList<>();
		loadHiscore();
		username = "default";
		paused = false;
		speedFaktor = 300;
		speed = speedFaktor;
		cheatSpeed = 50;
		spaceDown = false;
		wallFaktor = 10;
		food = new Food();
		yellowBonus = new Bonus(BonusType.YELLOW_BONUS);
		blueBonus = new Bonus(BonusType.BLUE_BONUS);
		magentaBonus = new Bonus(BonusType.MAGENTA_BONUS);
		greenBonus = new Bonus(BonusType.GREEN_BONUS);
		bonuses = new ArrayList<>();
		bonuses.add(blueBonus);
		bonuses.add(yellowBonus);
		bonuses.add(magentaBonus);
		bonuses.add(greenBonus);
		setGameOn(true);

		width = 250;
		wd = width / 10 - 2;
		height = 250;
		hg = height / 10 - 4;
		left = 10;
		right = width - 10;
		top = 30;
		bottom = height - 10;
		factor = 10;
		rnd = new Random();

		boardColorI = new Color(18, 199, 84);
		boardColorII = new Color(170, 199, 86);
		boardColor = boardColorII;
		switchBoardColor();
		gridColor = new Color(51, 51, 255);
		borderColor = new Color(255, 0, 0);
		setFoodPosition();
	}

	public void switchBoardColor() {
		if (boardColor == boardColorII)
			boardColor = boardColorI;
		else
			boardColor = boardColorII;

	}

	public ArrayList<HiScore> getHiscore() {
		return hiscore;
	}

	public void setHiscore(ArrayList<HiScore> hiscore) {
		this.hiscore = hiscore;
	}

	public void setHiscore() {
		Collections.sort(hiscore, new Comparator<HiScore>() {

			@Override
			public int compare(HiScore o1, HiScore o2) {
				return o2.getPoints() - o1.getPoints();
			}
		});
		if (hiscore.size() > 10)
			hiscore = new ArrayList<>(hiscore.subList(0, 10));
		saveHiscore();
	}

	public void loadHiscore() {
		if (hiscore == null) {
			hiscore = new ArrayList<>();
		} else {
			hiscore.clear();
		}
		try {
			FileInputStream fstream = new FileInputStream("data/hiscore.ssd");
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			while ((strLine = br.readLine()) != null) {
				HiScore hc = new HiScore(strLine);
				if (!hiscore.contains(hc))
					hiscore.add(hc);
			}
			br.close();
			in.close();
			fstream.close();

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (!offlineVersion) {
			try {
				File tmp = new File("data/hiscore_o.ssd");
				tmp.deleteOnExit();
				FileOutputStream fos = new FileOutputStream(tmp);
				ftpClient.retrieveFile("hiscore_o.ssd", fos);

				FileInputStream fstream = new FileInputStream(
						tmp.getAbsolutePath());
				DataInputStream in = new DataInputStream(fstream);
				BufferedReader br = new BufferedReader(
						new InputStreamReader(in));
				String strLine;
				while ((strLine = br.readLine()) != null) {
					HiScore hc = new HiScore(strLine);
					if (!hiscore.contains(hc))
						hiscore.add(hc);
				}
				br.close();
				in.close();
				fos.close();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		setHiscore();
	}

	public void saveHiscore() {
		try {
			BufferedWriter pw = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(new File("data/hiscore.ssd"))));
			for (HiScore hc : hiscore) {
				pw.write(hc.toString());
			}
			pw.close();

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (!offlineVersion) {
			try {
				ftpClient.deleteFile("hiscore_o.ssd");
				FileInputStream fis = new FileInputStream(new File(
						"data/hiscore.ssd"));
				ftpClient.storeFile("hiscore_o.ssd", fis);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public boolean isPaused() {
		return paused;
	}

	/**
	 * blabla
	 * 
	 * @author nvelickovic10
	 * @param paused
	 *            true/false
	 * */
	public void setPaused(boolean paused) {
		this.paused = paused;
	}

	/**
	 * returns the instance of the AppCore
	 * 
	 * @author nvelickovic10
	 * */
	public static AppCore getInstance() {
		if (instance == null)
			instance = new AppCore();
		return instance;
	}

	public int getWd() {
		return wd;
	}

	public int getHg() {
		return hg;
	}

	public void setFoodPosition() {
		food.setPosition(new Point(rnd.nextInt(wd) + 1, rnd.nextInt(hg) + 1));
		while (isFoodOnWall())
			food.setPosition(new Point(rnd.nextInt(wd) + 1, rnd.nextInt(hg) + 1));
		if (firstBonus) {
			firstBonus = false;
		} else {
			if (rnd.nextInt(15) % 5 == 0 && !yellowBonus.isOn())
				yellowBonus.setOn();
			if (rnd.nextInt(15) == 1 && !blueBonus.isOn())
				blueBonus.setOn();
			if (rnd.nextInt(15) % 5 == 0 && !magentaBonus.isOn())
				magentaBonus.setOn();
			if (rnd.nextInt(15) % 4 == 0 && !greenBonus.isOn())
				greenBonus.setOn();
		}
	}

	public void setBonusPosition(BonusType type) {
		Bonus bonus = null;
		if (type == BonusType.YELLOW_BONUS)
			bonus = yellowBonus;
		else if (type == BonusType.BLUE_BONUS)
			bonus = blueBonus;
		else if (type == BonusType.MAGENTA_BONUS)
			bonus = magentaBonus;
		else if (type == BonusType.GREEN_BONUS)
			bonus = greenBonus;

		bonus.setPosition(new Point(rnd.nextInt(wd) + 1, rnd.nextInt(hg) + 1));
		while (isBonusOnWall())
			bonus.setPosition(new Point(rnd.nextInt(wd) + 1,
					rnd.nextInt(hg) + 1));
	}

	private boolean isFoodOnWall() {
		for (SnakeBits bit : walls)
			if (bit.getPosition().getX() == food.getPosition().getX()
					&& bit.getPosition().getY() == food.getPosition().getY())
				return true;
		return false;
	}

	private boolean isBonusOnWall() {
		for (SnakeBits bit : walls)
			if (bit.getPosition().getX() == yellowBonus.getPosition().getX()
					&& bit.getPosition().getY() == yellowBonus.getPosition()
							.getY())
				return true;
		return false;
	}

	public void setFoodPosition(Point position) {
		food.setPosition(position);
	}

	public Color getBoardColor() {
		return boardColor;
	}

	public Color getGridColor() {
		return gridColor;
	}

	public Color getBorderColor() {
		return borderColor;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	public int getLeft() {
		return left;
	}

	public int getRight() {
		return right;
	}

	public int getTop() {
		return top;
	}

	public int getBottom() {
		return bottom;
	}

	public int getFactor() {
		return factor;
	}

	public boolean isGameOn() {
		return gameOn;
	}

	public void setGameOn(boolean gameOn) {
		this.gameOn = gameOn;
	}

	public int getPoints() {
		return points;
	}

	public void setPoints(int points) {
		this.points = points;
	}

	public boolean isDeveloperVersion() {
		return developerVersion;
	}

	public void setDeveloperVersion(boolean developerVersion) {
		this.developerVersion = developerVersion;
	}

	public Food getFood() {
		return food;
	}

	public Random getRnd() {
		return rnd;
	}

	public long getSpeed() {
		return speed;
	}

	public void returnSpeed() {
		this.speed = this.speedFaktor;
	}

	public void setSpeed() {
		this.speedFaktor *= 0.98;
		if (!spaceDown)
			this.speed = this.speedFaktor;
	}

	public void slowSpeed() {
		this.speedFaktor /= 0.98;
		if (!spaceDown)
			this.speed = this.speedFaktor;
	}

	public void setSpeed(long speed) {
		this.speed = speed;
	}

	public void setSpeedFaktor(long faktor) {
		this.speedFaktor = faktor;
		setSpeed(faktor);
	}

	public Engine getEngine() {
		return engine;
	}

	public void setEngine(Engine engine) {
		this.engine = engine;
	}

	public long getCheatSpeed() {
		return cheatSpeed;
	}

	public void setSpaceDown(boolean spaceDown) {
		this.spaceDown = spaceDown;
	}

	public GameWindow getGw() {
		return gw;
	}

	public void setGw(GameWindow gw) {
		this.gw = gw;
	}

	public void repaint() {
		gw.repaint();
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public boolean isLoading() {
		return loading;
	}

	public void setLoading(boolean loading) {
		this.loading = loading;
	}

	public ArrayList<SnakeBits> getWalls() {
		return walls;
	}

	public void setWalls(ArrayList<SnakeBits> walls) {
		this.walls = walls;
	}

	public int getWallFaktor() {
		return wallFaktor;
	}

	public void setWallFaktor(int wallFaktor) {
		this.wallFaktor = wallFaktor;
	}

	public boolean isOfflineVersion() {
		return offlineVersion;
	}

	public void setOfflineVersion(boolean offlineVersion) {
		this.offlineVersion = offlineVersion;
	}

	public ArrayList<Bonus> getBonuses() {
		return bonuses;
	}

	public void setBonuses(ArrayList<Bonus> bonuses) {
		this.bonuses = bonuses;
	}

	public Bonus getBonus(BonusType type) {
		switch (type) {
		case BLUE_BONUS:
			return blueBonus;
		case MAGENTA_BONUS:
			return magentaBonus;
		case YELLOW_BONUS:
			return yellowBonus;
		case GREEN_BONUS:
			return greenBonus;
		default:
			return null;
		}
	}

}
